﻿
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;


namespace ImageHash
{
    public class Imghash
    {
        // Calculate a hash of an image based on visual characteristics.
        // Described at http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
        // The exact value of the resulting hash depends on the scaling algorithms used by the runtime.
        public static ulong AverageHash(System.Drawing.Image theImage)
        {
            // Squeeze the image down to an 8x8 6-bit grayscale image.

            // Chant the ancient incantations to create the correct data structures.
            Bitmap squeezedImage = new Bitmap(8, 8, PixelFormat.Format32bppRgb);
            Graphics drawingArea = Graphics.FromImage(squeezedImage);
            drawingArea.CompositingQuality = CompositingQuality.HighQuality;
            drawingArea.InterpolationMode = InterpolationMode.HighQualityBilinear;
            drawingArea.SmoothingMode = SmoothingMode.HighQuality;
            drawingArea.DrawImage(theImage, 0, 0, 8, 8);
            byte[] grayScaleImage = new byte[64];

            uint averageValue = 0;
            ulong finalHash = 0;

            // Reduce the colour to 6-bit grayscale and calculate the average value.
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    // Add together the red, green and blue
                    // components and reduce them to 6 bit grayscale.
                    uint pixelColour = (uint)squeezedImage.GetPixel(x, y).ToArgb();
                    uint grayTone = (pixelColour & 0x00FF0000) >> 16;
                    grayTone += (pixelColour & 0x0000FF00) >> 8;
                    grayTone += (pixelColour & 0x000000FF);
                    grayTone /= 12;

                    grayScaleImage[x + y * 8] = (byte)grayTone;
                    averageValue += grayTone;
                }
            }
            averageValue /= 64;

            // Return 1-bits when the tone is equal to or above the average,
            // and 0-bits when it's below the average.
            for (int k = 0; k < 64; k++)
            {
                //if (k % 8 == 0)
                //    Console.WriteLine();

                if (grayScaleImage[k] >= averageValue)
                {
                    finalHash |= (1UL << (63 - k));
                    //Console.Write(" ");
                }
                //else
                //    Console.Write("#");
            }
            //Console.WriteLine();
            //Console.WriteLine();

            return finalHash;
        }


        // Count the number of 1-bits in a number.

        // We use a precomputed table to hopefully speed it up.
        // Made in Python as follows:
        // a = list()
        // a.append(0)
        // while len(a) <= 128:
        //  a.extend([b+1 for b in a])
        static byte[] bitCounts = {
	0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,
	2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,
	4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,
	3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
	4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 };

        public static uint BitCount(ulong theNumber)
        {
            uint count = 0;

            for (; theNumber > 0; theNumber >>= 8)
            {
                count += bitCounts[(theNumber & 0xFF)];
            }

            return count;
        }
    }

}
